﻿package taurus.client.io;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

import com.taurus.core.entity.ITObject;
import com.taurus.core.entity.TObject;
import com.taurus.core.util.ICallback;
import com.taurus.core.util.Logger;

import taurus.client.ISocketLayer;
import taurus.client.Message;
import taurus.client.NetManager;
import taurus.client.SocketCode;
import taurus.client.TaurusClient;
import taurus.client.TaurusClient.ConnectionProtocol;

/**
 * SocketEngine
 * 
 * @author daixiwei daixiwei15@126.com
 *
 */
public class SocketEngine implements ISocketLayer {
	private static final Logger		logger			= Logger.getLogger(SocketEngine.class);

	ISocket							_socket;
	private String					_sessionToken;
	private String					_serverAddress;
	int protocolCompression = 100;
	
	private ICallback<SocketCode>	_onConnect;
	ICallback<Message>		_onEvent;
	private Queue<Runnable>			_actionQueue	= new LinkedList<Runnable>();

	SystemController				_controller;
	LagMonitor						_lagMonitor;
	IOHandler						_packetHandler;
	TaurusClient _client;
	int sessionId;
	private ITObject _validate;
	
	public SocketEngine(TaurusClient client) {
		_client = client;
		_socket = new SocketTcp(this);
		_controller = new SystemController(this);
		_packetHandler = new IOHandler(this, _controller);
		_lagMonitor = new LagMonitor(this, NetManager.TIMEOUT_TIME, 10);
	}


	public void connect()
    {
		_validate = TObject.newInstance();
        _serverAddress = _client.getHost();
        _socket.connect();
    }

    public void connect(ITObject validate)
    {
        _validate = validate;
        _serverAddress = _client.getHost();
        _socket.connect();
    }
    

	@Override
	public void disconnect() {
		if(_socket!=null)
			_socket.disconnect();
		if (_lagMonitor != null)
			_lagMonitor.destroy();
		_lagMonitor = null;
	}

	@Override
	public void processEvent() {
		if (_socket.isConnected() && _controller.isTimeOut()) {
			if (_onConnect != null)
				_onConnect.action(SocketCode.TimeoutDisconnect);
			return;
		}

		while (true) {
			Runnable myAction;
			synchronized (_actionQueue) {
				if (_actionQueue.size() <= 0) {
					break;
				}
				myAction = _actionQueue.poll();
			}
			myAction.run();
		}
	}

	public void write(Message msg) {
		try {
			_controller.sendRequest(msg);
		} catch (IOException e) {
			logger.error(e);
		}
	}

	void onStatusChanged(SocketCode statusCode) {
		if (statusCode != SocketCode.Connect) {
			this.disconnect();
		}
		logger.info("status : " + statusCode);
		if (statusCode == SocketCode.Connect) {
			try {
				_controller.sendValidate(_validate);
			} catch (IOException e) {
				logger.error(e);
			}
		} else {
			if (_onConnect != null)
				_onConnect.action(statusCode);
		}
	}

	void onConnect() {
		_controller.refreshTime();
		_lagMonitor.start();
		if (_onConnect != null)
			_onConnect.action(SocketCode.Connect);
	}
	
	void onValidate(){
		onConnect();
    }

	void enqueueActionForDispatch(Runnable action) {
		synchronized (_actionQueue) {
			_actionQueue.offer(action);
		}
	}

	void enqueueStatusCallback(final SocketCode statusValue) {
		synchronized (_actionQueue) {
			_actionQueue.offer(new Runnable() {
				@Override
				public void run() {
					onStatusChanged(statusValue);
				}
			});
		}
	}




	public String getServerAddress() {
		return _serverAddress;
	}

	@Override
	public ConnectionProtocol getProtocol() {
		return ConnectionProtocol.Tcp;
	}
	
	public int getAveragePingTime() {
		return _lagMonitor != null ? _lagMonitor.getAveragePingTime() : 1000;
	}

	public void setSession(String session) {
		_sessionToken = session == null ? "" : session;
	}

	public String getSession() {
		return _sessionToken;

	}

	@Override
	public void setOnConnect(ICallback<SocketCode> onConnect) {
		_onConnect = onConnect;
	}

	@Override
	public void setOnEvent(ICallback<Message> onEvent) {
		_onEvent = onEvent;
	}

	public int getProtocolCompression() {
		return _client.getProtocolCompression();
	}
}
